//  Copyright Druid Mechanics


#include "Actor/AuraEffectActor.h"
#include "AbilitySystemComponent.h"
#include "GameplayEffect.h"
#include "AbilitySystemBlueprintLibrary.h"
#include "ActiveGameplayEffectHandle.h"
#include "Kismet/KismetMathLibrary.h"

/**
 * GE详解: 
 * LINK: https://rendertool.github.io/RenderDoc/unreal/core_%E6%A0%B8%E5%BF%83_/8.1-GASGE.html
 * LINK: https://supervj.top/2020/12/24/GameplayAbilitySystem%E5%88%86%E6%9E%90(%E5%85%AD)_GameplayEffect1/
 */

AAuraEffectActor::AAuraEffectActor()
{
	PrimaryActorTick.bCanEverTick = false;
	/**
	 * SceneComponent有转换和支持吸附，但没有渲染或碰撞能力。
	 * 可用作层次结构中的“虚拟”组件，以抵消其他组件。
	 */
	SetRootComponent(CreateDefaultSubobject<USceneComponent>("SceneRoot"));
}

// Called when the game starts or when spawned
void AAuraEffectActor::BeginPlay()
{
	Super::BeginPlay();
	InitialLocation = GetActorLocation();
	CalculatedLocation = InitialLocation;
	CalculatedRotation = GetActorRotation();
}

void AAuraEffectActor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	RunningTime += DeltaTime;
	const float SinePeriod = 2 * PI / SinePeriodConstant;
	if (RunningTime > SinePeriod)
	{
		RunningTime = 0.f;
	}
	ItemMovement(DeltaTime);
}

void AAuraEffectActor::StartSinusoidalMovement()
{
	bSinusoidalMovement = true;
	InitialLocation = GetActorLocation();
	CalculatedLocation = InitialLocation;
}

void AAuraEffectActor::StartRotation()
{
	bRotates = true;
	CalculatedRotation = GetActorRotation();
}

void AAuraEffectActor::ApplyEffectToTarget(AActor* TargetActor, TSubclassOf<UGameplayEffect> GameplayEffectClass)
{
	if (TargetActor->ActorHasTag(FName("Enemy")) && !bApplyEffectsToEnemies)
	{
		return;
	}
	/**
	 * UGameplayEffect本身只是一份配置数据，相当于一份模板。
	 * FGameplayEffectSpec就是这个GE配置模板的具体细节，目前理解是具体配置的规则。
	 * FGameplayEffectSpec产生的运行时具体实例就是FActiveGameplayEffect，多个实例的StackCount会累加，可以用来处理一些堆叠效果，跟溢出效果。
	 * FActiveGameplayEffectsContainer是用来存储FActiveGameplayEffect的容器，每一个ASC上会包含一个，大部分对GE具体实例处理的逻辑，也定义在FActiveGameplayEffectsContainer的中。
	 */
	/**
	 * GetAbilitySystemComponent: 尝试在此Actor查找ASC。
	 */
	UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(TargetActor);
	if (TargetASC == nullptr)
	{
		return;
	}
	check(GameplayEffectClass);
	/**
	 * MakeEffectContext: 为ASC的所有者创建一个EffectContext。
	 */
	/**
	 *  FGameplayEffectContextHandle: 封装FGameplayEffectContext或子类的句柄，以允许其多态并正确复制
	 */
	FGameplayEffectContextHandle EffectContextHandle = TargetASC->MakeEffectContext();
	/**
	 * AddSourceObject: 设置创建此效果的对象。
	 */
	EffectContextHandle.AddSourceObject(this);
	/**
	 * FGameplayEffectSpecHandle: 
	 * 允许蓝图生成一次GameplayEffectSpec，然后通过句柄引用它，以多次/多个目标应用它。
	 */
	/**
	 * FGameplayEffectSpec: FGameplayEffectSpec是可修改的。
	 * 我们从初始条件开始，并对其进行修改。
	 * 从这个意义上说，它是有状态/可变的，但它仍然与FGameplayEffectSpec的应用实例中的FActiveGameplayEffect不同。
	 * LINK: https://zhuanlan.zhihu.com/p/464329488
	 */
	/**
	 * MakeOutgoingSpec: 获取一个准备应用于其他事物的FGameplayEffectSpec的句柄。
	 */
	/**
	 * MakeOutgoingSpec的第二个参数(Level)可以设置效果等级，也就是说GE的等级，目前可用来修改Curve数据。
	 */
	const FGameplayEffectSpecHandle EffectSpecHandle = TargetASC->MakeOutgoingSpec(
		GameplayEffectClass, ActorLevel, EffectContextHandle);
	/**
	 * ApplyGameplayEffectSpecToSelf: 申请将GameplayEffectSpec到自身(目前猜测是给攻击对象添加GameplayEffect)。
	 */
	/**
	 * Data: TSharedPtr<FGameplayEffectSpec> Data。
	 */
	const FActiveGameplayEffectHandle ActiveEffectHandle = TargetASC->ApplyGameplayEffectSpecToSelf(
		*EffectSpecHandle.Data.Get());
	/**
	 * Def: GE定义。此Spec指向的静态数据，目前理解是蓝图里的FGameplayEffect里的配置项数据。
	 */
	const bool bIsInfinite = EffectSpecHandle.Data.Get()->Def.Get()->DurationPolicy ==
		EGameplayEffectDurationType::Infinite;
	if (bIsInfinite && InfiniteEffectRemovalPolicy == EEffectRemovalPolicy::RemovalOnEndOverlap)
	{
		ActiveEffectHandles.Add(ActiveEffectHandle, TargetASC);
	}
	if (bDestroyOnEffectApplication && !bIsInfinite)
	{
		Destroy();
	}
}

void AAuraEffectActor::OnOverlap(AActor* TargetActor)
{
	if (TargetActor->ActorHasTag(FName("Enemy")) && !bApplyEffectsToEnemies)
	{
		return;
	}
	if (InstantEffectApplicationPolicy == EEffectApplicationPolicy::ApplyOnOverlap)
	{
		ApplyEffectToTarget(TargetActor, InstantGameplayEffectClass);
	}
	if (DurationEffectApplicationPolicy == EEffectApplicationPolicy::ApplyOnOverlap)
	{
		ApplyEffectToTarget(TargetActor, DurationGameplayEffectClass);
	}
	if (InfiniteEffectApplicationPolicy == EEffectApplicationPolicy::ApplyOnOverlap)
	{
		ApplyEffectToTarget(TargetActor, InfiniteGameplayEffectClass);
	}
}

void AAuraEffectActor::OnEndOverlap(AActor* TargetActor)
{
	if (TargetActor->ActorHasTag(FName("Enemy")) && !bApplyEffectsToEnemies)
	{
		return;
	}
	if (InstantEffectApplicationPolicy == EEffectApplicationPolicy::ApplyOnEndOverlap)
	{
		ApplyEffectToTarget(TargetActor, InstantGameplayEffectClass);
	}
	if (DurationEffectApplicationPolicy == EEffectApplicationPolicy::ApplyOnEndOverlap)
	{
		ApplyEffectToTarget(TargetActor, DurationGameplayEffectClass);
	}
	if (InfiniteEffectApplicationPolicy == EEffectApplicationPolicy::ApplyOnEndOverlap)
	{
		ApplyEffectToTarget(TargetActor, InfiniteGameplayEffectClass);
	}
	if (InfiniteEffectRemovalPolicy == EEffectRemovalPolicy::RemovalOnEndOverlap)
	{
		UAbilitySystemComponent* TargetASC = UAbilitySystemBlueprintLibrary::GetAbilitySystemComponent(TargetActor);
		/**
		 * IsValid: 测试对象是否有效，如果这个对象不是non-null或not pending kill 或 garbage collision，则返回true。
		 */
		if (!IsValid(TargetASC))
		{
			return;
		}
		/**
		 * 这里是用一个数组暂存所需要删除的，因为直接删除会报错。
		 */
		TArray<FActiveGameplayEffectHandle> HandleToRemove;
		for (TPair<FActiveGameplayEffectHandle, UAbilitySystemComponent*> HandlePair : ActiveEffectHandles)
		{
			/**
			 * 在这里直接删除TPair<FActiveGameplayEffectHandle, UAbilitySystemComponent*>键值对程序会崩溃。
			 */
			if (TargetASC == HandlePair.Value)
			{
				/**
				 * RemoveActiveGameplayEffect: 通过FActiveGameplayEffectHandle移除GameplayEffect。StacksToRemove=-1将移除所有堆叠。
				 */
				TargetASC->RemoveActiveGameplayEffect(HandlePair.Key, -1);
				HandleToRemove.Add(HandlePair.Key);
			}
		}
		for (auto& Handle : HandleToRemove)
		{
			/**
			 *  FindAndRemoveChecked: 寻找特定key的键值对，如果从Map中找到就移除它并返回这个键值对的值。
			 */
			ActiveEffectHandles.FindAndRemoveChecked(Handle);
		}
	}
}

void AAuraEffectActor::ItemMovement(float DeltaTime)
{
	if (bRotates)
	{
		const FRotator DeltaRotation(0.f, DeltaTime * RotationRate, 0.f);
		/**
		 * ComposeRotators: 将两个旋转操作进行组合，其结果是先应用旋转A，再应用旋转B后得到的最终旋转效果。
		 * NOTE: 相当于CalculatedRotation = CalculatedRotation + DeltaRotation
		 */
		CalculatedRotation = UKismetMathLibrary::ComposeRotators(CalculatedRotation, DeltaRotation);
		if (bSinusoidalMovement)
		{
			const float Sine = SineAmplitude * FMath::Sin(RunningTime * SinePeriodConstant);
			CalculatedLocation = InitialLocation + FVector(0.f, 0.f, Sine);
		}
	}
}
